/**
 * Yet Another Editable Plugin (YAEP)
 * 
 * Copyright (c) 2009 Jared Hocutt
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * @version 1.0
 * 
 * @param fieldType (string):
 * 		defines the type of field to use when editing ('text' and 'textarea' are
 * 		supported)
 * 
 * @param defaultText (string):
 * 		defines the default text to display when a field is blank to denote that
 * 		it is editable
 * 
 * @param addClassOnSave (string):
 * 		defines the classes to add to the original element after a save action
 * 
 * @param removeClassOnSave (string):
 * 		defines the classes to remove from the original element after a save action
 * 
 * @param required (boolean):
 * 		defines if content is required in order to save or if blank text is valid
 * 
 * @param showSaveButton (boolean):
 * 		defines if the save button is visible (if not, onBlur will save)
 * 
 * @param showCancelButton (boolean):
 * 		defines if the cancel button is visible (if not and save button is visible,
 * 		onBlur will cancel, otherwise there is no cancel functionality)
 * 
 * @param showHand (boolean):
 * 		defines the the hand (cursor: pointer) should be shown when mousing over the
 * 		editable element
 */
jQuery.fn.editable = function(options) {
	
	// define the default settings
	var defaults = {
		fieldType: 'textarea',
		defaultText: 'click to edit',
		addClassOnSave: '',
		removeClassOnSave: '',
		required: false,
		showSaveButton: true,
		showCancelButton: true,
		showHand: true
	};
	
	// override defaults with options set by the user
	var settings = jQuery.extend({}, defaults, options);
	
	// escape the HTML in the string passed to the function
	String.prototype.escapeHTML = function() {
		return this.replace(/&/g, '&amp;')
				.replace(/</g, '&lt;')
				.replace(/>/g, '&rt;')
				.replace(/"/g, '&quot;');
	};
	
	var buttonHTML = '';
	if (settings.showSaveButton) { // create the save button
		buttonHTML += '<button type="button" class="yaep-save">Save</button>';
	}
	if (settings.showCancelButton) { // create the cancel button
		buttonHTML += '<button class="yaep-cancel">Cancel</button>';
		
	}
	
	// setup the actions to use for saving and cancelling
	var saveMethod = '';
	var cancelMethod = '';
	if (settings.showSaveButton && settings.showCancelButton) { // save and cancel buttons are visible
		saveMethod = 'click';
		cancelMethod = 'click';
	} else if (!settings.showSaveButton && settings.showCancelButton) { // only cancel button is visible
		saveMethod = 'blur';
		cancelMethod = 'click';
	} else if (settings.showSaveButton && !settings.showCancelButton) { // only save button is visible
		saveMethod = 'click';
		cancelMethod = 'blur';
	} else { // no buttons are visible (there will not be a way to cancel)
		saveMethod = 'blur';
		cancelMethod = '';
	}
	
	return this.each(function() { // iterate through each element
		var originalElement = jQuery(this);
		var editing = false;
		var clickCount = 0;
		
		if (settings.showHand) {
			var originalCursor = originalElement.css('cursor'); // save the original cursor for restore
			
			originalElement.hover( // show the hand (cursor: pointer) on mouseover
				function() {
					originalElement.css('cursor', 'pointer');
				},
				function() {
					originalElement.css('cursor', originalCursor);
				}
			);
		}
		
		if (jQuery.trim(originalElement.html()).length == 0) { // the original element is blank
			originalElement.html(settings.defaultText); // set the default text
		}
		
		function clickAction() {
			clickCount++; // increment the click count
			
			if (!editing) { // begin editing if not already editing
				editing = true;
				
				var originalContent = jQuery.trim(originalElement.html()).escapeHTML(); // save the original content in case the user cancels
				var originalText = jQuery.trim(originalElement.text()); // get the original text to use for editing (leaves out html tags)
				
				if (originalContent == settings.defaultText) { // set the original content to blank if it matches the default text
					originalContent = '';
				}
				
				// determine the html to use to for showing the edit fields
				var editFieldHTML = '';
				if (settings.fieldType = 'textarea') {
					editFieldHTML = '<textarea name="yaepValue" class="yaep-field" rows="3" cols="15">' + originalText + '</textarea>';
				} else if (settings.fieldType = 'text') {
					editFieldHTML = '<input type="text" name="yaepValue" class="yaep-field" value="' + originalText + '"/>';
				} else {
					editFieldHTML = 'An invalid fieldType (' + settings.fieldType + ') was specified.';
				}
				
				if (buttonHTML.length > 0) { // there are buttons to show
					originalElement.html(editFieldHTML + '<br />' + buttonHTML); // display the edit field and button(s)
				} else { // there are not any buttons to show
					originalElement.html(editFieldHTML); // display the edit field
				}
			}
			
			var editField = $('.yaep-field', originalElement); // get the edit field that was created
			
			if (clickCount == 1) { // only do the following on the first click (prevents doing the following every click)
				function cancelAction() {
					editing = false; // reset the editing flag
					clickCount = 0; // reset the click count
					
					originalElement.html(originalContent); // restore the original content
					
					return false;
				}
				
				function saveAction() {
					editing = false; // reset the editing flag
					clickCount = 0; // reset the click count
					
					originalElement.html(editField.val()); // replace the existing content with the new content
					
					if (settings.removeClassOnSave.length > 0) { // remove the class(es) specified
						originalElement.removeClass(settings.removeClassOnSave);
					}
					
					if (settings.addClassOnSave.length > 0) { // add the class(es) specified
						originalElement.addClass(settings.addClassOnSave);
					}
					
					return false;
				}
				
				editField
					.focus() // give the edit field focus
					.select() // select all text in the field
					.width(editField.parent().width()); // set the width to fill the parent
				
				if (saveMethod == 'click') {
					$('.yaep-save', originalElement).click(saveAction);
				} else if (saveMethod == 'blur') {
					editField.blur(saveAction);
				}
				
				if (cancelMethod == 'click') {
					$('.yaep-cancel', originalElement).click(cancelAction);
				} else if (cancelMethod == 'blur') {
					editField.blur(cancelAction);
				}
			}
		}
		
		originalElement.click(clickAction);
	});
};